package gnss

import (
	"dgo/goutils/utils"
	"fmt"
	"time"
)

/*
*

信息类型 DF002 uint12 12
跟踪站 ID DF003 uint12 12
BeiDou Epoch Time (TOW) DF427 is used for GNSS Epoch Time.
GNSS 历元时间 —— uint30 30  DF 号每个GNSS 单独声明
MSM 当前历元信息结束标志位 DF393 bit(1) 1    // １：该条消息不是该参考站当前历元的结尾；	０：该条消息是该参考站当前历元的结尾；
IODS-跟踪站期号 DF409 uint3 3 目前保留，置“0”
保留 DF001 bit(7) 7 保留可能用于GNSS
站钟调整策略标识 DF411 uint2 2   // Clock Steering NaviIndicator
外钟使用情况标识 DF412 uint2 2   // External Clock NaviIndicator
GNSS 平滑类型 DF417 bit(1) 1    // GNSS Smoothing Type NaviIndicator
GNSS 平滑间隔 DF418 bit(3) 3
GNSS 卫星掩码字 DF394 bit(64) 64  // GNSS Satellite Mask
GNSS 信号掩码字 DF395 bit(32) 32
GNSS 数据单元掩码字 DF396 bit(X) X(X≤64)

BITS = 169 + X

SigN * SatN 不能大于 64, cellMask 是8字节, 矩阵存储 位数

SatMask 0~63位, 0位1 表示 64号卫星

	satMask >> (64 - N) & 1 == 1 表示N卫星存在

SigMask 0~31,

	sigMask >> (32 - N) & 1 == 1 表示N 信号存在

CellMask 0~N, (N < 64, N 是SatMaskN * SigMaskN

	cellMask >> N & 1 == 1 表示 存在的第一颗 的第一个信号有值
*/
type RtcmRawMsmHeader struct {
	NaviSys      byte
	MsgID        uint16
	StaID        uint16 // 跟踪站ID
	EpochT       uint32 //  RTCM中的原始数据
	EpochEndFlag bool
	IODS         byte
	Reserved     byte
	ClkSteering  byte

	ExternalClk       byte
	SmoothingType     byte
	SmoothingInterval byte
	SatN              byte
	SatMask           uint64 // 卫星最大64颗  小端法存储
	SigN              byte
	SignalMask        uint32 // 信号最大32颗  小端法存储
	CellN             byte
	CellMask          uint64 //  X=Nsig*Nsat <=64     // 小端法存储

	EncodeType byte // 0:使用hc模式, 1:qx, 2:yd(最大12颗)
}

/*
*

	sigid 1..32
*/
func (this *RtcmRawMsmHeader) DelSigFromMask(SigID byte) {
	m := U32_1 << (31 - (SigID - 1))
	m = ^m
	this.SignalMask &= m
	this.SigN = utils.GetBitOnU32(this.SignalMask)
}

func (this *RtcmRawMsmHeader) DelSigsFromMask(sigids ...byte) {
	for i := 0; i < len(sigids); i++ {
		m := U32_1 << (31 - (sigids[i] - 1))
		m = ^m
		this.SignalMask &= m
	}
	this.SigN = utils.GetBitOnU32(this.SignalMask)
}

func (this *RtcmRawMsmHeader) SetSigsToMask(sigids ...byte) {
	for i := 0; i < len(sigids); i++ {
		m := U32_1 << (31 - (sigids[i] - 1))
		this.SignalMask |= m
	}
	this.SigN = utils.GetBitOnU32(this.SignalMask)
}

func (this *RtcmRawMsmHeader) DelSigsFromMaskEx(except_sigids ...byte) {
	m := U32_0
	for i := 0; i < len(except_sigids); i++ {
		m |= U32_1 << (31 - (except_sigids[i] - 1))
	}
	this.SignalMask &= m
	this.SigN = utils.GetBitOnU32(this.SignalMask)
}

func (this *RtcmRawMsmHeader) SetSigMask(sigMask uint32) {
	this.SignalMask = sigMask
	this.SigN = utils.GetBitOnU32(this.SignalMask)
}

func (this *RtcmRawMsmHeader) DelSatsFromMask(satids ...byte) {
	m := U64_0
	for i := 0; i < len(satids); i++ {
		m |= U64_1 << (63 - (satids[i] - 1))
	}
	this.SatMask &= ^m
	this.SatN = utils.GetBitOnU64(this.SatMask)
}

func (this *RtcmRawMsmHeader) DelSatsFromMaskEx(startPrn, endPrn byte) {
	m := U64_0
	if endPrn < startPrn || endPrn == 0 {
		endPrn = 64
	}
	if startPrn == 0 {
		startPrn = 1
	}

	for i := startPrn; i <= endPrn; i++ {
		m |= U64_1 << (63 - (i - 1))
	}
	this.SatMask &= ^m
	this.SatN = utils.GetBitOnU64(this.SatMask)
}

/*
**

	prn 取值范围 1..64

	统计从startPrn开始到endPrn, 存在有的卫星数n后prn是多少(newEndPrn)
	endPrn表示探测最多到的编号 0表示到64

	如果newEndPrn为0表示没有startPrn到endPrn没有存在有数据的卫星
*/
func (this *RtcmRawMsmHeader) StatSatOn(startPrn, endPrn byte, n byte) (newEndPrn byte) {
	m := this.SatMask
	if endPrn < startPrn || endPrn == 0 {
		endPrn = 64
	}
	if startPrn == 0 {
		startPrn = 1
	}

	newEndPrn = 0

	for i := startPrn; i <= endPrn; i++ {
		v1 := utils.GetBitValue0(m, 64-i)
		if v1 == 1 {
			newEndPrn = i
			n--
			if n == 0 {
				break
			}
		}
	}
	return newEndPrn
}

func (this *RtcmRawMsmHeader) ExtractGloTime() (dow byte, sod uint32) {
	dow = byte(this.EpochT >> 27)
	bitN := (32 - 30) + 3
	sod = this.EpochT << bitN >> bitN
	return
}

func (this *RtcmRawMsmHeader) EncodeGloTime(dow byte, sod uint32) {
	this.EpochT = uint32(dow)<<27 + sod
}

/*
**

	tow 周内毫秒
*/
func (this *RtcmRawMsmHeader) SetTOW(tow uint32) {
	navisys := this.NaviSys
	tow1 := int32(tow)
	if navisys == SYS_BD {
		tow1 -= 14 * 1000
		if tow1 < 0 { // 应该是上周尾部
			tow1 = tow1 + MILL_SECONDS_PER_WEEK
		} else if tow1 >= MILL_SECONDS_PER_WEEK {
			tow1 -= MILL_SECONDS_PER_WEEK
		}
		this.EpochT = uint32(tow1)
	} else if navisys == SYS_GLO {
		tow1 += (SECONDS_PER_HOUR * 3 * 1000)
		tow1 -= LEAP_SECONDS * 1000

		if tow1 < 0 { // 应该是上周尾部
			tow1 = tow1 + MILL_SECONDS_PER_WEEK
		} else if tow1 >= MILL_SECONDS_PER_WEEK {
			tow1 -= MILL_SECONDS_PER_WEEK
		}

		sod := tow1 % MILL_SECONDS_PER_DAY
		dow := tow1 / MILL_SECONDS_PER_DAY
		this.EpochT = uint32(dow)<<27 + uint32(sod)
	} else {
		this.EpochT = uint32(tow1)
	}
}

/*
**

	获取sow(tow)周内毫秒
*/
func (this *RtcmRawMsmHeader) GetTOW() uint32 {
	navsys := MsgID2NaviSys(int(this.MsgID))
	var tow int32
	if navsys == SYS_GLO {
		dow, sod := this.ExtractGloTime()
		tow = int32(dow)*int32(SECONDS_PER_DAY)*1000 + int32(sod)
		tow -= int32(SECONDS_PER_HOUR * 3 * 1000) // GLONASS 历元时间是 GLONASS ICD 定义的系统时间为 UTC（SU）+3.0hours.
		tow += LEAP_SECONDS * 1000
	} else if navsys == SYS_BD {
		tow = int32(this.EpochT)
		tow += 14 * 1000
	} else {
		tow = int32(this.EpochT)
	}

	if tow < 0 { // 应该是上周尾部 [BUG]
		tow = tow + MILL_SECONDS_PER_WEEK
	} else if tow >= MILL_SECONDS_PER_WEEK {
		tow -= MILL_SECONDS_PER_WEEK
	}
	return uint32(tow)
}

func (this *RtcmRawMsmHeader) GetGPSTime(rt time.Time) time.Time {
	tow := this.GetTOW()
	return TOW2Time(rt, uint(tow))
}

func (this *RtcmRawMsmHeader) Encode(bb *utils.BytesBuilder, startIdx int) (encodeBitN int) {
	encodeBitN = 0
	bb.CheckSize(startIdx, 30) // (169 + 64) / 8

	bb.SetBitU16(startIdx, encodeBitN, 12, this.MsgID)
	encodeBitN += 12
	bb.SetBitU16(startIdx, encodeBitN, 12, this.StaID)
	encodeBitN += 12
	bb.SetBitU32(startIdx, encodeBitN, 30, this.EpochT)
	encodeBitN += 30
	if this.EpochEndFlag {
		bb.SetBitU8(startIdx, encodeBitN, 1, 0)
		encodeBitN += 1
	} else {
		bb.SetBitU8(startIdx, encodeBitN, 1, 1)
		encodeBitN += 1
	}
	bb.SetBitU8(startIdx, encodeBitN, 3, this.IODS)
	encodeBitN += 3
	bb.SetBitU8(startIdx, encodeBitN, 7, this.Reserved)
	encodeBitN += 7
	bb.SetBitU8(startIdx, encodeBitN, 2, this.ClkSteering)
	encodeBitN += 2
	bb.SetBitU8(startIdx, encodeBitN, 2, this.ExternalClk)
	encodeBitN += 2
	bb.SetBitU8(startIdx, encodeBitN, 1, this.SmoothingType)
	encodeBitN += 1
	bb.SetBitU8(startIdx, encodeBitN, 3, this.SmoothingInterval)
	encodeBitN += 3
	bb.SetBitU64(startIdx, encodeBitN, 64, this.SatMask)
	encodeBitN += 64
	bb.SetBitU32(startIdx, encodeBitN, 32, this.SignalMask)
	encodeBitN += 32
	n := int(this.SatN * this.SigN)
	bb.SetBitU64(startIdx, encodeBitN, n, this.CellMask)
	encodeBitN += n
	return
}

func (this *RtcmRawMsmHeader) Decode(buf []byte) (decodeBitN int, err error) {
	decodeBitN = 0
	bitsTotalN := len(buf) * 8
	if bitsTotalN < 169 {
		return 0, fmt.Errorf("BUF小于169 bits")
	}
	this.MsgID = GetBitU16(buf, decodeBitN, 12)
	this.NaviSys = MsgID2NaviSys(int(this.MsgID))
	decodeBitN += 12
	this.StaID = GetBitU16(buf, decodeBitN, 12)
	decodeBitN += 12
	this.EpochT = GetBitU(buf, decodeBitN, 30)
	decodeBitN += 30

	if GetBitU8(buf, decodeBitN, 1) == 0 { // 0结尾
		this.EpochEndFlag = true
	} else {
		this.EpochEndFlag = false
	}
	decodeBitN += 1

	this.IODS = GetBitU8(buf, decodeBitN, 3)
	decodeBitN += 3
	this.Reserved = GetBitU8(buf, decodeBitN, 7)
	decodeBitN += 7 // 保留

	this.ClkSteering = GetBitU8(buf, decodeBitN, 2)
	decodeBitN += 2
	this.ExternalClk = GetBitU8(buf, decodeBitN, 2)
	decodeBitN += 2
	this.SmoothingType = GetBitU8(buf, decodeBitN, 1)
	decodeBitN += 1
	this.SmoothingInterval = GetBitU8(buf, decodeBitN, 3)
	decodeBitN += 3

	{
		u64 := GetBitU64(buf, decodeBitN, 64)
		decodeBitN += 64
		this.SatMask = u64
		this.SatN = utils.GetBitOnU64(u64)
	}

	{ // 信号
		u32 := GetBitU(buf, decodeBitN, 32)
		decodeBitN += 32
		this.SignalMask = u32
		this.SigN = utils.GetBitOnU32(u32)
	}

	{

		// 10011001100110011001
		n := int(this.SatN * this.SigN)
		if bitsTotalN < 169+n {
			return 0, fmt.Errorf("BUF小于169 + %d bits", n)
		}
		u64 := GetBitU64(buf, decodeBitN, n)
		decodeBitN += n
		this.CellMask = u64
		this.CellN = utils.GetBitOnU64(this.CellMask)
	}

	return
}

func (this *RtcmRawMsmHeader) SetSats(sats []byte) {
	for i := U8_0; i < byte(len(sats)); i++ {
		satIdx := sats[i] - 1
		this.SatMask |= 1 << (63 - satIdx)
	}
	this.SatN = utils.GetBitOnU64(this.SatMask)
}

func (this *RtcmRawMsmHeader) HasValue(satId, sigId byte) bool {
	sat0 := this.SatMask >> (64 - satId) & 1
	if sat0 == 0 {
		return false
	}

	sig0 := this.SignalMask >> (32 - sigId) & 1
	if sig0 == 0 {
		return false
	}

	return true
}

func (this *RtcmRawMsmHeader) ExtractSats() []byte {
	u64 := this.SatMask
	sats := make([]byte, this.SatN)
	j := U8_0
	for i := U8_0; i < 64; i++ {
		v := u64 >> (63 - i) & 1
		if v == 1 {
			sats[j] = i + 1
			j++
		}
	}
	return sats
}

func (this *RtcmRawMsmHeader) ExtractSigs() []byte {
	rval := make([]byte, this.SigN)
	u32 := this.SignalMask
	j := 0
	for i := U8_0; i < 32; i++ {
		v := u32 >> (31 - i) & 1
		if v == 1 {
			rval[j] = i + 1
			j++
		}
	}
	return rval
}

func (this *RtcmRawMsmHeader) CloneFrom(src *RtcmRawMsmHeader) {
	this.MsgID = src.MsgID
	this.StaID = src.StaID
	this.EpochT = src.EpochT
	this.EpochEndFlag = src.EpochEndFlag
	this.IODS = src.IODS
	this.ClkSteering = src.ClkSteering
	this.ExternalClk = src.ExternalClk
	this.SmoothingType = src.SmoothingType
	this.SmoothingInterval = src.SmoothingInterval
	this.SatN = src.SatN
	this.SatMask = src.SatMask
	this.SigN = src.SigN
	this.SignalMask = src.SignalMask
	this.CellN = src.CellN
	this.CellMask = src.CellMask
}

func (this *RtcmRawMsmHeader) PrintMask() string {

	naviSys := MsgID2NaviSys(int(this.MsgID))

	var bb utils.BytesBuilder
	bb.AppendStr("sat:")
	sats := this.ExtractSats()
	for i := U8_0; i < this.SatN; i++ {
		bb.Appendf("%.2d ", sats[i])
	}
	bb.AppendStr("\r\n")

	bb.AppendStr("sig:")
	sigs := this.ExtractSigs()
	for i := U8_0; i < this.SigN; i++ {
		bb.Appendf("%s ", SigiRinexCode(naviSys, sigs[i]))
	}
	bb.AppendStr("\r\n")

	{
		bb.AppendStr("cell:\n")
		sigs := this.ExtractSigs()
		bb.AppendStr("   ")
		for i := U8_0; i < this.SigN; i++ {
			bb.Appendf("%s\t", SigiRinexCode(naviSys, sigs[i]))
		}
		bb.AppendStr("\r\n")
		n := int(this.SatN * this.SigN)
		u64 := this.CellMask
		j := 0
		for i := 0; i < n; i++ {
			if byte(i)%this.SigN == 0 {
				bb.Appendf("%.2d ", sats[j])
				j++
			}
			v := (u64 >> (n - 1 - i)) & 1
			bb.Appendf("%d\t", v)
			if i > 0 && (byte(i+1)%this.SigN == 0) {
				bb.AppendStr("\r\n")
			}
		}
	}
	return bb.String()
}

func (this *RtcmRawMsmHeader) Details() string {

	naviSys := MsgID2NaviSys(int(this.MsgID))
	var bb utils.BytesBuilder
	bb.Appendf("msgid:%d, t:%d, Clock Steering/External Clock:%d/%d\r\n", this.MsgID, this.EpochT, this.ClkSteering, this.ExternalClk)
	bb.Appendf("\tSmoothing Type(Interval):%d/%d\r\n", this.SmoothingType, this.SmoothingInterval)

	bb.AppendStr("sat:")
	sats := this.ExtractSats()
	for i := U8_0; i < this.SatN; i++ {
		bb.Appendf("%.2d ", sats[i])
	}
	bb.AppendStr("\r\n")

	bb.AppendStr("sig:")
	sigs := this.ExtractSigs()
	for i := U8_0; i < this.SigN; i++ {
		bb.Appendf("%s ", SigiRinexCode(naviSys, sigs[i]))
	}
	bb.AppendStr("\r\n")
	return bb.String()
}
